`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2025/11/05 22:45:33
// Design Name: 
// Module Name: npu
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module npu(
    input wire clk,
    input wire rst_n,
    input wire start_npu,  // npu开始信号
    input wire data_input_end, // fifo_input数据输入结束信号
    
    input wire [7:0] data_in,
    output wire [7:0] data_out,
    output wire done_npu,

    output wire state_input_monitor
    );
    // fifo_input
    reg wr_en_input;
    reg rd_en_input;
    wire [7:0] dout_input;
    wire full_input;
    wire empty_input;
    wire [9:0] data_count_input;

    // fifo_output
    reg wr_en_output;
    reg rd_en_output;
    reg [7:0] din_output;
    wire full_output;
    wire empty_output;
    wire [9:0] data_count_output;

    // fc_top
    reg rst_n_fc;
    reg [5:0] count_fc;
    reg [7:0] addr_input_ram;
    reg [1023:0] data_input_ram;
    reg wren_input_ram;
    reg start_fc;
    wire [7:0] fc1_output_data;
    wire fc1_output_wren;
    wire fc1_done;
    wire [7:0] fc2_output_data;
    wire fc2_output_wren;
    wire fc2_done;
    wire [7:0] fc3_output_data;
    wire fc3_output_wren;
    wire fc3_done;
    wire done_o;

    fc_top u_fc_top(
        .clk(clk),
        .rst_n_fc(rst_n_fc),
        .count(count_fc),
        .addr_input_ram(addr_input_ram),
        .data_input_ram(data_input_ram),
        .wren_input_ram(wren_input_ram),
        .start(start_fc),
        .done_o(done_o),
        .fc1_output_data_monitor(fc1_output_data),
        .fc1_output_wren_monitor(fc1_output_wren),
        .fc1_done_monitor(fc1_done),
        .fc2_output_data_monitor(fc2_output_data),
        .fc2_output_wren_monitor(fc2_output_wren),
        .fc2_done_monitor(fc2_done),
        .fc3_output_data_monitor(fc3_output_data),
        .fc3_output_wren_monitor(fc3_output_wren),
        .fc3_done_monitor(fc3_done)
    );

    fifo_input u_fifo_input(
        .clk(clk),
        .srst(~rst_n),
        .din(data_in),
        .wr_en(wr_en_input),
        .rd_en(rd_en_input),
        .dout(dout_input),
        .full(full_input),
        .empty(empty_input),
        .data_count(data_count_input)
    );

    fifo_output u_fifo_output(
        .clk(clk),
        .srst(~rst_n),
        .din(din_output),
        .wr_en(wr_en_output),
        .rd_en(rd_en_output),
        .dout(data_out),
        .full(full_output),
        .empty(empty_output),
        .data_count(data_count_output)
    );

        // 逻辑控制信号定义
    wire [7:0] r_addr_input; // 输入RAM把数据输出到神经网络的输出地址,即读地址
    wire [7:0] w_addr_input; // 输入RAM的数据写入地址,也就是fifo_input把数据写入RAM的地址


    // inputRAM两个地址的引入
    // ?这里引入值的加1减1是对的吗
    assign r_addr_input = 4 * (count_fc + 1); // *是最终数据的前置值,每一次使用的是4个地址的数据,这里表示的是这4个地址的初始地址
    assign w_addr_input = addr_input_ram;


    // 状态定义
    parameter IDLE = 4'b0000, DATA_IN = 4'b0001, WAIT_DONE = 4'b0010, WAIT_DATA = 4'b0011, CALCULATOR = 4'b0100,
              WAIT_OUTPUT = 4'b0101, OUT_FC1 = 4'b0110, OUT_FC2 = 4'b0111, OUT_FC3 = 4'b1000;


    // !----------------------- 1. 数据输入状态转移 ---------------------------
    reg [3:0] state_input;
    reg [3:0] next_input;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state_input <= IDLE;
        end else begin
            state_input <= next_input;
        end
    end
    // *状态转移
    always @(*) begin
        case (state_input)
            IDLE: next_input = start_npu ? DATA_IN : IDLE;
            DATA_IN: begin
                if (!empty_input || !data_input_end) begin
                    next_input = DATA_IN;
                end else begin
                    next_input = WAIT_DONE;
                end
            end
            WAIT_DONE: next_input = done_npu ? IDLE : WAIT_DONE;
            default: next_input = IDLE;
        endcase
    end


    // *DATA_IN状态下的数据输入过程
    // 使能信号的控制
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_en_input <= 0;
            rd_en_input <= 0;
        end
        else begin
            if (state_input == DATA_IN) begin
                if (!full_input && !data_input_end) begin
                    wr_en_input <= 1;
                end else begin
                    wr_en_input <= 0;
                end
                // !读使能除了非空判断,还有就是地址不相等判断 
                // ?当数据超出RAM 的地址限制之后,如果FIFO的输入已经快到RAM的输出地址(一般不会出现)这里的逻辑需要更改
                if (!empty_input) begin
                    rd_en_input <= 1;
                end else begin
                    rd_en_input <= 0;
                end
            end else begin
                wr_en_input <= 0;
                rd_en_input <= 0;
            end
        end
    end
    // 向RAM输入的地址和数据的控制
    reg [6:0] count_ram; // 128次fifo的输出才能组合为一个输入到RAM

    // fifo的输入和输出会存在延迟,设置2级的延迟
    reg [7:0] addr_input_ram_delay[1:0];
    reg [7:0] wren_input_ram_delay[1:0];
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            count_ram <= 0;
            wren_input_ram <= 0;
            addr_input_ram_delay[0] <= 7'b0;
            wren_input_ram_delay[0] <= 7'b0;
            addr_input_ram_delay[1] <= 7'b0;
            wren_input_ram_delay[1] <= 7'b0;
            // addr_input_ram_delay[2] <= 7'b0;
            // wren_input_ram_delay[2] <= 7'b0;
            // addr_input_ram_delay[3] <= 7'b0;
            // wren_input_ram_delay[3] <= 7'b0;
            data_input_ram <= 0;
        end else begin
            if (state_input == DATA_IN) begin
                if (rd_en_input) begin
                    data_input_ram <= {data_input_ram[1015:0], dout_input};
                    count_ram <= count_ram + 1;
                    // ?这里的时序逻辑是对的吗
                    if (count_ram == 7'd127) begin
                        wren_input_ram_delay[1] <= 1;
                        count_ram <= 0;
                    end else begin
                        wren_input_ram_delay[1] <= 0;
                    end
                    // ?地址的增加要延后一个时钟周期
                    if (wren_input_ram_delay[1]) begin
                        addr_input_ram_delay[1] <= addr_input_ram_delay[1] + 1;
                    end else begin
                        addr_input_ram_delay[1] <= addr_input_ram_delay[1];
                    end
                end
            end else begin
                addr_input_ram_delay[1] <= addr_input_ram_delay[1];
                wren_input_ram_delay[1] <= 0;
            end
        end
    end
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            addr_input_ram <= 0;
            wren_input_ram <= 0;
        end else begin
            addr_input_ram_delay[0] <= addr_input_ram_delay[1];
            wren_input_ram_delay[0] <= wren_input_ram_delay[1];
            addr_input_ram <= addr_input_ram_delay[0];
            wren_input_ram <= wren_input_ram_delay[0];
        end
    end



    // !----------------------- 2. 数据运算状态转移 ---------------------------
    reg [3:0] state_fc;
    reg [3:0] next_fc;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state_fc <= IDLE;
        end else begin
            state_fc <= next_fc;
        end
    end

    // 从RAM读取地址和数据的控制
    wire ram_write_enable; // 等待RAM中存储有8位数据后再进行RAM数据读取
    // ?这里假设了fifo数据的输入远远比RAM中读取数据进行运算快
    assign ram_write_enable = w_addr_input > 7 || data_input_end ? 1'b1 : 1'b0;

    // *状态转移
    always @(*) begin
        case (state_fc)
            IDLE: next_fc = start_npu ? WAIT_DATA : IDLE;
            // !这里直接假设了fifo数据的输入比神经网络使用数据的运算快的多
            WAIT_DATA: next_fc = ram_write_enable ? CALCULATOR : WAIT_DATA;
            CALCULATOR: next_fc = done_o && empty_input && r_addr_input == w_addr_input ? WAIT_OUTPUT : CALCULATOR;
            WAIT_OUTPUT: next_fc = done_npu ? IDLE : WAIT_OUTPUT;
            default: next_fc = IDLE;
        endcase
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            count_fc <= 0;
            start_fc <= 0;
            rst_n_fc <= 0;
        end else begin
            if (state_fc == CALCULATOR) begin
                if (done_o) begin
                    count_fc <= count_fc + 1;
                    rst_n_fc <= 0;
                    start_fc <= 0;
                end else begin
                    rst_n_fc <= 1;
                    start_fc <= rst_n_fc ? 1'b1 : 1'b0;
                end
            end else begin
                count_fc <= 0;
                start_fc <= 0;
            end
        end
    end


    // !-------------------------- 3. 数据输出状态转移 ---------------------------
    reg [3:0] state_output;
    reg [3:0] next_output;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state_output <= IDLE;
        end else begin
            state_output <= next_output;
        end
    end

    // *状态转移
    always @(*) begin
        case (state_output)
            IDLE: next_output = start_fc ? OUT_FC1 : IDLE;
            OUT_FC1: next_output = fc1_done ? OUT_FC2 : OUT_FC1;
            OUT_FC2: next_output = fc2_done ? OUT_FC3 : OUT_FC2;
            OUT_FC3: next_output = fc3_done ? IDLE : OUT_FC3;
            default: next_output = IDLE;
        endcase
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_en_output <= 0;
            rd_en_output <= 0;
            din_output <= 0;
        end else begin
            case (state_output)
                OUT_FC1: begin
                    if (fc1_output_wren && !full_output) begin
                        wr_en_output <= 1;
                        din_output <= fc1_output_data;
                    end else begin
                        wr_en_output <= 0;
                    end
                end
                OUT_FC2: begin
                    if (fc2_output_wren && !full_output) begin
                        wr_en_output <= 1;
                        din_output <= fc2_output_data;
                    end else begin
                        wr_en_output <= 0;
                    end
                end
                OUT_FC3: begin
                    if (fc3_output_wren && !full_output) begin
                        wr_en_output <= 1;
                        din_output <= fc3_output_data;
                    end else begin
                        wr_en_output <= 0;
                    end
                end
                default: begin
                    wr_en_output <= 0;
                end
            endcase
        end
    end

    // !❌,这里的rd_en_output的控制逻辑有问题,需要修改,现在只是在进行针对性输出的修改
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rd_en_output <= 0;
        end else begin
            // if (state_output == OUT_FC1 || state_output == OUT_FC2 || state_output == OUT_FC3 ) begin
            if (!empty_output) begin
                rd_en_output <= 1;
            end else begin
                rd_en_output <= 0;
            end
            // end else begin
            //     rd_en_output <= 0;  
            // end
        end
    end




    // !运算结束信号定义
    // 一切完成后进行8轮延迟,确保一切完毕
    reg [7:0]done_npu_reg;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            done_npu_reg <= 8'b00000000;
        end else begin
            done_npu_reg <= {done_npu_reg[6:0], state_input == WAIT_DONE && state_fc == WAIT_OUTPUT && state_output == IDLE};
        end
    end
    assign done_npu = done_npu_reg[7];

endmodule
